﻿using System.Reflection;

using GCC.Container.Abstracts;
using GCC.Container.Common;
using GCC.Container.Common.Models;
using GCC.Container.LogProviders;

namespace GCC.Container.PluginLoader
{
    /// <summary>
    /// 管理器
    /// </summary>
    public partial class Manager
    {

        private readonly List<PluginInstance> _items = new();
        /// <summary>
        /// 当前依赖的抽象版本
        /// </summary>
        private readonly Version _absVersion = new("10000.0.0.0");
        /// <summary>
        /// 依赖的抽象库名称
        /// </summary>
        private readonly string _absName = "";

        private Manager()
        {
            if (Assembly.GetAssembly(typeof(AbsEntrance)) is Assembly asb)
            {
                if (asb.GetCustomAttribute(typeof(AssemblyInformationalVersionAttribute)) is AssemblyInformationalVersionAttribute asbVerInfo)
                {
                    _absVersion = new Version(asbVerInfo.InformationalVersion);
                    _absName = asb.GetName().Name ?? "";
                }
            }
            this.LoadFromConfig();
        }

        /// <summary>
        /// 从配置文件加载
        /// </summary>
        private void LoadFromConfig()
        {
            ContainerConfigManager.Instance.Config.Plugins.ForEach(item =>
            {
                this.LoadPlugin(item);
            });
        }

        /// <summary>
        /// 加载指定路径
        /// </summary>
        /// <param name="folderPath"></param>
        private void LoadPlugin(PluginItem plugin)
        {
            var filePath = Path.IsPathRooted(plugin.FilePath) ? plugin.FilePath : Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, plugin.FilePath));

            if (!File.Exists(filePath))
            {
                _logger.Warning($"File {filePath} not exists, plugin:{plugin.Name} load failed.");
                return;
            }
            var fileSha256 = FileOperations.SHA256Encrypt(filePath);
            if (!string.IsNullOrEmpty(plugin.Sha256) && !plugin.Sha256.Equals(fileSha256))
            {
                _logger.Warning($"File {filePath}'s sha 256 invalid.");
                return;
            }
            try
            {

                var temp = Assembly.LoadFrom(filePath);

                var refAsb = temp.GetReferencedAssemblies();
                foreach (var item in refAsb)
                {

                    if (item.Name == this._absName && item.Version < this._absVersion)
                    {
                        if (item.Version.Major != this._absVersion.Major)
                        {
                            _logger.Warning($"Skip load pliugin {plugin.Name}, cause it's GCC.Container.Abstracts Major version is:{item.Version.Major}, but current container's Major version is:{this._absVersion.Major}.");

                            return;
                        }
                        else if (item.Version.Minor != this._absVersion.Minor)
                        {
                            _logger.Warning($"Skip load pliugin {plugin.Name}, cause it's GCC.Container.Abstracts Minor version is:{item.Version.Minor}, but current container's Minor version is:{this._absVersion.Minor}.");
                            return;
                        }
                        else if (item.Version.Build != this._absVersion.Build || item.Version.Revision != this._absVersion.Revision)
                        {
                            _logger.Information($"Pliugin {plugin.Name}, it's GCC.Container.Abstracts version is:{item.Version}, but current container's version is:{this._absVersion}, check them, it maybe has some bug.");
                        }
                    }
                }

                var instance = temp.CreateInstance(plugin.EntranceTypeDesc);
                if (instance is null)
                {
                    _logger.Warning($"Plugin {plugin.Name}'s enterance type:{plugin.EntranceTypeDesc} create failed.");
                    return;
                }

                if (instance is AbsEntrance enterance)
                {
                    enterance.SetLoggerProvider(GCCLoggerProvider.Instance!);

                    if (enterance.NeedsApiContainerVersion() > ContainerConfigManager.CurrentVersion)
                    {
                        _logger.Warning($"Current container Version was too old, the Plugin:{plugin.Name} needs:{enterance.NeedsApiContainerVersion}");
                        return;
                    }

                    var pluginVersion = temp.GetCustomAttribute(typeof(AssemblyInformationalVersionAttribute));

                    if (pluginVersion is AssemblyInformationalVersionAttribute asbVerInfo)
                    {
                        var pluginInfo = new PluginInstance()
                        {
                            IsRelease = enterance.IsRelease(),
                            Name = plugin.Name,
                            PluginFilePath = filePath,
                            Sha256 = fileSha256 ?? "Unknow",
                            Version = new Version(asbVerInfo.InformationalVersion),
                            RawAssembly = temp,
                            EnteranceInstance = enterance
                        };
                        var allTypes = temp.GetExportedTypes();
                        foreach (var type in allTypes)
                        {
                            var grpcAttr = type.GetCustomAttribute(typeof(GCCGRPCServerAttribute), true);
                            var apiAttr = type.GetCustomAttribute(typeof(GCCAPIServerAttribute), true);
                            var jobAttr = type.GetCustomAttribute(typeof(GCCJobAttribute), true);
                            if (grpcAttr is not null)
                            {
                                pluginInfo.AllGrpcServices.Add(type);
                            }
                            if (apiAttr is not null)
                            {
                                pluginInfo.AllApi.Add(type);
                            }
                            if (jobAttr is not null)
                            {
                                pluginInfo.AllJobs.Add(type);
                            }
                        }



                        this._items.Add(pluginInfo);
                        _logger.Trace($"Plugin {plugin.Name} load completed.");
                    }
                    else
                    {
                        _logger.Warning($"Plugin {plugin.Name} load Faild. It's version info was invalid");
                    }


                }
                else
                {
                    _logger.Warning($"The Plugin:{plugin.Name}'s enterance wasn't type:{typeof(AbsEntrance)} or version not XXXX");
                    return;
                }
            }
            catch (Exception ex)
            {
                _logger.Error($"load plugin {plugin.Name} error, got {ex.Message}.");
            }
        }
    }
}
